package ace.module.weixin.core.impl.manager.impl;

import ace.cmp.core.constants.CoreConstant;
import ace.cmp.core.utils.SystemClockUtils;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.spring.AbstractSpringDtoManager;
import ace.cmp.id.generator.api.LongIdGenerator;
import ace.module.weixin.api.enums.WxsLockStatusEnum;
import ace.module.weixin.api.model.dto.WxsLockDto;
import ace.module.weixin.core.impl.dao.entity.QWxsLock;
import ace.module.weixin.core.impl.dao.entity.WxsLock;
import ace.module.weixin.core.impl.dao.repository.WxsLockRepository;
import ace.module.weixin.core.impl.manager.WxsLockManager;
import java.util.Objects;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Component;

/**
 * 数据库锁 spring data jdbc querydsl rsql manager implement.
 *
 * @author Caspar
 */
@Slf4j
@AllArgsConstructor
@Component
public class WxsLockManagerImpl
    extends AbstractSpringDtoManager<WxsLock, Long, WxsLockDto>
    implements WxsLockManager {

  private final WxsLockRepository wxsLockRepository;
  private final LongIdGenerator idGenerator;

  @Override
  public boolean lock(String key, String ownerId) {
    WxsLock wxsLock = wxsLockRepository.getByKeyAndDeleteFlag(key, CoreConstant.DELETE_FLAG_NOT);
    if (Objects.isNull(wxsLock)) {
      return this.lockForInsert(key, ownerId);
    } else {
      return this.lockForUpdate(key, ownerId);
    }
  }

  @Override
  public boolean unlock(String key, String ownerId) {
    return this.wxsLockRepository.update(sqlUpdateClause -> {
      QWxsLock qwxsLock = QWxsLock.wxsLock;
      return sqlUpdateClause
          .set(qwxsLock.status, WxsLockStatusEnum.RELEASE.getCode())
          .set(qwxsLock.ownerId, StringUtils.EMPTY)
          .set(qwxsLock.updateTime, SystemClockUtils.currentTimeMillis())
          .where(qwxsLock.key.eq(key))
          .where(qwxsLock.ownerId.eq(ownerId))
          .where(qwxsLock.deleteFlag.eq(CoreConstant.DELETE_FLAG_NOT))
          .execute();
    }) > 0;
  }


  private boolean lockForInsert(String key, String ownerId) {
    WxsLock wxsLock = WxsLock.builder()
        .id(idGenerator.get())
        .ownerId(ownerId)
        .key(key)
        .status(WxsLockStatusEnum.LOCKED.getCode())

        .deleteFlag(CoreConstant.DELETE_FLAG_NOT)
        .createTime(System.currentTimeMillis())
        .updateTime(System.currentTimeMillis())
        .rowVersion(CoreConstant.DEFAULT_ROW_VERSION)
        .build();
    try {
      return this.wxsLockRepository.insert(wxsLock) > 0;
    } catch (DuplicateKeyException ex) {
      return false;
    } catch (Exception ex) {
      log.error("插入锁异常", ex);
      return false;
    }
  }


  private boolean lockForUpdate(String key, String ownerId) {
    return this.wxsLockRepository.update(sqlUpdateClause -> {
      QWxsLock qwxsLock = QWxsLock.wxsLock;
      return sqlUpdateClause
          .set(qwxsLock.status, WxsLockStatusEnum.LOCKED.getCode())
          .set(qwxsLock.ownerId, ownerId)
          .set(qwxsLock.rowVersion, CoreConstant.DEFAULT_ROW_VERSION)
          .set(qwxsLock.updateTime, SystemClockUtils.currentTimeMillis())
          .where(qwxsLock.key.eq(key))
          .where(qwxsLock.status.eq(WxsLockStatusEnum.RELEASE.getCode()))
          .where(qwxsLock.deleteFlag.eq(CoreConstant.DELETE_FLAG_NOT))
          .execute();
    }) > 0;
  }
}
